---
title: "Direct Tool Calls"
description: "Call MCP tools programmatically without AI"
icon: "phone"
---

Direct tool calls enable you to invoke MCP server tools programmatically without an LLM or agent. This is perfect when you know exactly what tool to call and don't need AI to make decisions.

## When to Use Direct Tool Calls

Use direct tool calls when you need:

- **Deterministic execution**: Known tool with known parameters
- **Programmatic control**: Integrate MCP tools into existing applications
- **No AI overhead**: Simple tool invocation without LLM reasoning
- **Custom workflows**: Build your own tool orchestration logic

<Warning>
**Sampling Limitation**: Direct tool calls don't support tools that require sampling/completion capabilities, as these need an LLM to generate responses.
</Warning>

## Use Cases

Direct tool calls are ideal for:

- **Automation scripts**: Scheduled tasks and workflows
- **API wrappers**: Simplify MCP tool access
- **Testing**: Verify tool behavior programmatically
- **Integration**: Connect MCP tools to existing systems

## Basic Example

Here's how to call tools directly using the MCPClient:

<CodeGroup>
```typescript TypeScript
import { MCPClient } from 'mcp-use'

async function callToolExample() {
    // Configure the MCP server
    const config = {
        mcpServers: {
            everything: {
                command: 'npx',
                args: ['-y', '@modelcontextprotocol/server-everything']
            }
        }
    }

    // Create client from configuration
    const client = new MCPClient(config)

    try {
        // Initialize all configured sessions
        await client.createAllSessions()

        // Get the session for a specific server
        const session = client.getSession('everything')

        // List available tools
        const tools = await session.listTools()
        const toolNames = tools.map(t => t.name)
        console.log(`Available tools: ${toolNames}`)

        // Call a specific tool with arguments
        const result = await session.callTool(
            'add',
            { a: 1, b: 2 }
        )

        // Handle the result
        if (result.isError) {
            console.error(`Error: ${result.content}`)
        } else {
            console.log(`Tool result: ${result.content}`)
            console.log(`Text result: ${result.content[0].text}`)
        }

    } finally {
        // Clean up resources
        await client.closeAllSessions()
    }
}

callToolExample().catch(console.error)
```

```typescript TypeScript
import { MCPClient } from 'mcp-use'

async function callToolExample() {
    // Configure the MCP server
    const config = {
        mcpServers: {
            everything: {
                command: 'npx',
                args: ['-y', '@modelcontextprotocol/server-everything']
            }
        }
    }

    // Create client from configuration
    const client = new MCPClient(config)

    try {
        // Initialize all configured sessions
        await client.createAllSessions()

        // Get the session for a specific server
        const session = client.getSession('everything')

        // List available tools
        const tools = await session.listTools()
        const toolNames = tools.map(t => t.name)
        console.log(`Available tools: ${toolNames}`)

        // Call a specific tool with arguments
        const result = await session.callTool(
            'add',
            { a: 1, b: 2 }
        )

        // Handle the result
        if (result.isError) {
            console.error(`Error: ${result.content}`)
        } else {
            console.log(`Tool result: ${result.content}`)
            console.log(`Text result: ${result.content[0].text}`)
        }

    } finally {
        // Clean up resources
        await client.closeAllSessions()
    }
}

callToolExample().catch(console.error)
```
</CodeGroup>

## Working with Tool Results

The `call_tool` method returns a `CallToolResult` object with the following attributes:

- **`content`**: A list of `ContentBlock` objects containing the tool's output
- **`structuredContent`**: A dictionary with the structured result (for non-sampling tools)
- **`isError`**: Boolean indicating if the tool call encountered an error

### Accessing Results

<CodeGroup>
```typescript TypeScript
// Call a tool
const result = await session.callTool(
    'get_weather',
    { location: 'San Francisco' }
)

// Check for errors
if (result.isError) {
    console.error(`Tool error: ${result.content}`)
} else {
    // Access text content
    const textResult = result.content[0].text
    console.log(`Weather: ${textResult}`)

    // Access structured content if available
    if (result.structuredContent) {
        const structured = result.structuredContent
        console.log(`Temperature: ${structured.temperature}`)
    }
}
```

```typescript TypeScript
// Call a tool
const result = await session.callTool(
    'get_weather',
    { location: 'San Francisco' }
)

// Check for errors
if (result.isError) {
    console.error(`Tool error: ${result.content}`)
} else {
    // Access text content
    const textResult = result.content[0].text
    console.log(`Weather: ${textResult}`)

    // Access structured content if available
    if (result.structuredContent) {
        const structured = result.structuredContent
        console.log(`Temperature: ${structured.temperature}`)
    }
}
```
</CodeGroup>

## Timeout Configuration

For long-running tools, especially those that trigger sampling or LLM operations, you may need to configure custom timeout settings:

<CodeGroup>
```typescript TypeScript
// Extended timeout for long-running operations
const result = await session.callTool(
    'process_large_dataset',
    { dataset: 'large-data.json' },
    {
        timeout: 300000, // 5 minutes
    }
)
```

```typescript TypeScript
// Timeout with progress reset for operations that send progress notifications
// This allows the timeout to reset whenever the server sends a progress update
const result = await session.callTool(
    'analyze_with_llm',
    { text: 'Long text to analyze...' },
    {
        timeout: 60000, // 1 minute initial timeout
        resetTimeoutOnProgress: true, // Reset timeout on progress notifications
        maxTotalTimeout: 600000 // Maximum 10 minutes total
    }
)
```
</CodeGroup>

Available timeout options:
- **`timeout`**: Request timeout in milliseconds (default: 60000 / 60 seconds)
- **`maxTotalTimeout`**: Maximum total time in milliseconds, even with progress resets
- **`resetTimeoutOnProgress`**: If `true`, resets the timeout counter when a progress notification is received (default: `false`)
- **`signal`**: An `AbortSignal` to programmatically cancel the request

**Note**: When `resetTimeoutOnProgress` is enabled and the server sends progress notifications (like `ctx.sample()` does automatically), the operation can run indefinitely as long as progress is reported regularly.

## Multiple Server Example

You can work with multiple MCP servers and call tools from each:

<CodeGroup>
```typescript TypeScript
import { MCPClient } from 'mcp-use'

async function multiServerExample() {
    const config = {
        mcpServers: {
            filesystem: {
                command: 'npx',
                args: ['-y', '@modelcontextprotocol/server-filesystem'],
                env: { FILE_PATH: '/tmp' }
            },
            time: {
                command: 'npx',
                args: ['-y', '@modelcontextprotocol/server-time']
            }
        }
    }

    const client = new MCPClient(config)

    try {
        await client.createAllSessions()

        // Call tool from filesystem server
        const fsSession = client.getSession('filesystem')
        const files = await fsSession.callTool(
            'list_files',
            { path: '/tmp' }
        )
        console.log(`Files: ${files.content[0].text}`)

        // Call tool from time server
        const timeSession = client.getSession('time')
        const currentTime = await timeSession.callTool(
            'get_current_time',
            {}
        )
        console.log(`Current time: ${currentTime.content[0].text}`)

    } finally {
        await client.closeAllSessions()
    }
}

multiServerExample().catch(console.error)
```

```typescript TypeScript
import { MCPClient } from 'mcp-use'

async function multiServerExample() {
    const config = {
        mcpServers: {
            filesystem: {
                command: 'npx',
                args: ['-y', '@modelcontextprotocol/server-filesystem'],
                env: { FILE_PATH: '/tmp' }
            },
            time: {
                command: 'npx',
                args: ['-y', '@modelcontextprotocol/server-time']
            }
        }
    }

    const client = new MCPClient(config)

    try {
        await client.createAllSessions()

        // Call tool from filesystem server
        const fsSession = client.getSession('filesystem')
        const files = await fsSession.callTool(
            'list_files',
            { path: '/tmp' }
        )
        console.log(`Files: ${files.content[0].text}`)

        // Call tool from time server
        const timeSession = client.getSession('time')
        const currentTime = await timeSession.callTool(
            'get_current_time',
            {}
        )
        console.log(`Current time: ${currentTime.content[0].text}`)

    } finally {
        await client.closeAllSessions()
    }
}

multiServerExample().catch(console.error)
```
</CodeGroup>

## Discovering Available Tools

Before calling tools, you may want to discover what's available:

<CodeGroup>
```typescript TypeScript
async function discoverTools() {
    const client = new MCPClient(config)
    await client.createAllSessions()

    try {
        const session = client.getSession('my_server')

        // Get all tools
        const tools = await session.listTools()

        for (const tool of tools) {
            console.log(`Tool: ${tool.name}`)
            console.log(`  Description: ${tool.description}`)

            // Print input schema if available
            if (tool.inputSchema) {
                console.log(`  Parameters: ${JSON.stringify(tool.inputSchema)}`)
            }
            console.log()
        }

    } finally {
        await client.closeAllSessions()
    }
}
```

```typescript TypeScript
async function discoverTools() {
    const client = new MCPClient(config)
    await client.createAllSessions()

    try {
        const session = client.getSession('my_server')

        // Get all tools
        const tools = await session.listTools()

        for (const tool of tools) {
            console.log(`Tool: ${tool.name}`)
            console.log(`  Description: ${tool.description}`)

            // Print input schema if available
            if (tool.inputSchema) {
                console.log(`  Parameters: ${JSON.stringify(tool.inputSchema)}`)
            }
            console.log()
        }

    } finally {
        await client.closeAllSessions()
    }
}
```
</CodeGroup>

## Error Handling

Always handle potential errors when making direct tool calls:

<CodeGroup>
```typescript TypeScript
async function safeToolCall() {
    try {
        const result = await session.callTool(
            'some_tool',
            { param: 'value' }
        )

        if (result.isError) {
            // Handle tool-specific errors
            const errorMsg = result.content?.[0]?.text || 'Unknown error'
            console.error(`Tool returned error: ${errorMsg}`)
            return null
        }

        return result.content[0].text

    } catch (error) {
        // Handle connection or other errors
        console.error(`Failed to call tool: ${error}`)
        return null
    }
}
```

```typescript TypeScript
async function safeToolCall() {
    try {
        const result = await session.callTool(
            'some_tool',
            { param: 'value' }
        )

        if (result.isError) {
            // Handle tool-specific errors
            const errorMsg = result.content?.[0]?.text || 'Unknown error'
            console.error(`Tool returned error: ${errorMsg}`)
            return null
        }

        return result.content[0].text

    } catch (error) {
        // Handle connection or other errors
        console.error(`Failed to call tool: ${error}`)
        return null
    }
}
```
</CodeGroup>

## Limitations

When using direct tool calls, be aware of these limitations:

1. **No Sampling Support**: Tools that require sampling/completion (like text generation) won't work without an LLM
2. **Manual Tool Selection**: You need to know which tool to call - there's no automatic selection
3. **No Context Management**: Unlike agents, direct calls don't maintain conversation context
4. **Parameter Validation**: You're responsible for providing correct parameters

## Complete Example

View the complete working example in the repository:

[examples/direct_tool_call.py](https://github.com/pietrozullo/mcp-use/blob/main/examples/direct_tool_call.py)

## Next Steps

- Learn about [tool discovery](/typescript/client/tools) to explore available tools
- Understand [connection types](/typescript/client/connection-types) for different server configurations
- Explore [building custom agents](/typescript/advanced/building-custom-agents) for more complex use cases
